package com.hyt.it.ogt.pj.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.io.FileUtil;
import com.hyt.core.constant.CommonConstant;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.it.ogt.pj.enums.SubjectiveItemTypeEnum;
import com.hyt.it.ogt.common.config.GlobalConfig;
import com.hyt.it.ogt.pj.feign.KwClient;
import com.hyt.it.ogt.pj.feign.tps.TpsClient;
import com.hyt.it.ogt.pj.mapper.PaperItemMapper;
import com.hyt.it.ogt.pj.mapper.PaperMapper;
import com.hyt.it.ogt.pj.mapper.ProjectMapper;
import com.hyt.it.ogt.pj.mapper.StudentAnswerMapper;
import com.hyt.it.ogt.pj.mapper.StudentMapper;
import com.hyt.it.ogt.pj.mapper.SubjectMapper;
import com.hyt.it.ogt.pj.mapper.SubjectScoreMapper;
import com.hyt.it.ogt.pj.model.entity.Paper;
import com.hyt.it.ogt.pj.model.entity.PaperItem;
import com.hyt.it.ogt.pj.model.entity.Project;
import com.hyt.it.ogt.pj.model.entity.Student;
import com.hyt.it.ogt.pj.model.entity.StudentAnswer;
import com.hyt.it.ogt.pj.model.entity.Subject;
import com.hyt.it.ogt.pj.model.vo.ObjectiveItemScoreVO;
import com.hyt.it.ogt.pj.model.vo.ObjectiveScoreVO;
import com.hyt.it.ogt.pj.model.vo.PaperItemVo;
import com.hyt.it.ogt.pj.model.vo.download.InterfaceDocVo;
import com.hyt.it.ogt.pj.model.vo.download.ObjectiveScoreVo;
import com.hyt.it.ogt.pj.model.vo.download.StudentAnswerVo;
import com.hyt.it.ogt.pj.model.vo.download.StudentVo;
import com.hyt.it.ogt.pj.model.vo.download.SubjectVo;
import com.hyt.it.ogt.pj.model.vo.download.TopicItemVO;
import com.hyt.it.ogt.pj.model.vo.download.TopicVO;
import com.hyt.it.ogt.pj.service.IProjectDataDownLoadService;
import com.hyt.it.ogt.common.utils.ExecutorPoolUtil;
import com.hyt.it.ogt.common.utils.PjBeanUtils;
import com.hyt.model.tps.vo.Bucket;
import com.obs.services.ObsClient;
import com.obs.services.ObsConfiguration;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;

/**
 */
@Slf4j
@Service
public class ProjectDataDownLoadServiceImpl extends BaseServiceImpl<ProjectMapper, Project> implements IProjectDataDownLoadService {
	private final Logger logger = LoggerFactory.getLogger(ProjectDataDownLoadServiceImpl.class);

	@Resource
	private StudentMapper studentMapper;

	@Resource
	private SubjectScoreMapper subjectScoreMapper;

	@Resource
	private SubjectMapper subjectMapper;

	@Resource
	private PaperItemMapper paperItemMapper;

	@Resource
	private PaperMapper paperMapper;

	@Resource
	private StudentAnswerMapper studentAnswerMapper;

	@Autowired
	private ImageServiceImpl imageService;

	@Autowired
	private PaperServiceImpl paperService;

	@Resource
	private KwClient kwClient;
	
	@Autowired
	private TpsClient tpsClient;
	@Resource
	GlobalConfig globalConfig;
	
	private static ObsClient obsClient;

	@Override
	public List<SubjectVo> getSubjectVos(String projectId, String officeId) throws Exception {
		List<Paper> list = paperMapper.selectByProjectOrderByUpdateGroupByPaperId(projectId);
		List<SubjectVo> voList = new ArrayList<>();
		for (Paper paper : list) {
			SubjectVo vo = new SubjectVo();
			String paperId = paper.getPaperId();
			vo.setSubjectId(paperId);
			vo.setSubjectName(paper.getPaperName());
			vo.setPaperId(paper.getId());
			voList.add(vo);
		}
		return voList;
	}
	
	@Override
	public List<SubjectVo> getDkbSubjectVos(String projectId, String officeId) throws Exception {
		List<Subject> subjects = subjectMapper.selectByProjectId(projectId);
		List<SubjectVo> voList = new ArrayList<>();
		for (Subject subject : subjects) {
			// 通过科目id获取试卷
			String sjPaperId = paperMapper.getSjPaperIdByProjectIdAndSubject(projectId, subject.getSubjectId());
			// 通过科目id获取试卷
			String paperId = paperMapper.getPaperIdByProjectIdAndSubject(projectId, subject.getSubjectId());
			SubjectVo vo = new SubjectVo();
			vo.setOrgSubjectId(subject.getSubjectId());
			vo.setSubjectId(subject.getSubjectId());
			vo.setSubjectName(subject.getSubjectName());
			vo.setPaperId(paperId);
			vo.setSjPaperId(sjPaperId);
			voList.add(vo);
		}
		return voList;
	}
	
	
	

	@Override
	public List<ObjectiveScoreVo> getObjectiveScoreVos(String projectId) throws Exception {
		//考生
		LambdaQueryWrapper<Student> studentQueryWrapper = new LambdaQueryWrapper<>();
		studentQueryWrapper.eq(Student::getProjectId, projectId);
		List<Student> students = studentMapper.selectList(studentQueryWrapper);
		if (CollectionUtils.isEmpty(students)) {
			return Collections.emptyList();
		}
		//试卷包情况
		List<Paper> packageList = new ArrayList<Paper>();
		
        Set<String> packageIds  = students.stream().filter(s -> StringUtils.isNotEmpty(s.getPackageId())).map(s -> s.getPackageId()).collect(Collectors.toSet());
        if(!CollectionUtils.isEmpty(packageIds)) {
        	 LambdaQueryWrapper<Paper> paperQueryWrapper = new LambdaQueryWrapper<>();
             paperQueryWrapper.eq(Paper::getProjectId,projectId).in(Paper::getPackageId,packageIds);
             packageList = paperMapper.selectList(paperQueryWrapper);
        }
        List<ObjectiveScoreVo> vos = new ArrayList<>(students.size());
        //判断为试卷包
        if(!CollectionUtils.isEmpty(packageList)) {
			Map<String, List<Paper>> paperMap = packageList.stream().collect(Collectors.groupingBy(p -> p.getProjectId() + p.getSubjectId() + p.getPackageId()));
            for (Student student : students) {
				if(CollectionUtils.isEmpty(paperMap) ){
					continue;
				}
				List<Paper> papers = paperMap.get(student.getProjectId() + student.getSubjectId() + student.getPackageId());
				if(CollectionUtils.isEmpty(papers) ){
					continue;
				}
                for (Paper paper :papers) {
                    ObjectiveScoreVo vo = new ObjectiveScoreVo();
                    vo.setAdmissionNum(student.getAdmissionNum());
                    vo.setStudentName(student.getStudentName());
					String subjectId = paper.getPaperId();
					vo.setSubjectId(StringUtils.isNotEmpty(subjectId) ? subjectId.substring(0, 6) : null);
					ApiResponse<ObjectiveScoreVO> apiResponse = kwClient.getKgScoreForPaper(student.getProjectId(), student.getAdmissionNum(), paper.getPaperId(), paper.getPaperVersion() + "");
					if(null != apiResponse && null != apiResponse.getData()){
                        vo.setObjectiveScore(apiResponse.getData().getKgScore());
						vo.setObjectiveScoreStr(getObjectiveScoreStr(apiResponse.getData().getObjectiveItemScoreList()));
                        vo.setObjectiveOmrStr(getObjectiveOmrStr(apiResponse.getData().getObjectiveItemScoreList()));
					}
                    vos.add(vo);
                }
            }
        }else{
            LambdaQueryWrapper<Paper> paperWrapper = new LambdaQueryWrapper<>();
            paperWrapper.eq(Paper::getProjectId, projectId);
            List<Paper> papers = paperMapper.selectList(paperWrapper);
            Map<String, Paper> paperMap = papers.stream().collect(Collectors.toMap(p -> (p.getProjectId() + p.getSubjectId()+p.getId()), Paper -> Paper, (key1, key2) -> key2));
			for (Student student : students) {
				if (null == student) {
					continue;
				}
                ObjectiveScoreVo vo = new ObjectiveScoreVo();
                vo.setAdmissionNum(student.getAdmissionNum());
				vo.setStudentName(student.getStudentName());
				if (!CollectionUtils.isEmpty(paperMap)
						&& null != paperMap.get(student.getProjectId() + student.getSubjectId()+student.getPaperId())) {
					Paper paper = paperMap.get(student.getProjectId() + student.getSubjectId()+student.getPaperId());
                    String subjectId = paper.getPaperId();
                    vo.setSubjectId(StringUtils.isNotEmpty(subjectId) ? subjectId.substring(0, 6) : null);
					ApiResponse<ObjectiveScoreVO> apiResponse = kwClient.getKgScoreForPaper(student.getProjectId(), student.getAdmissionNum(), paper.getPaperId(), paper.getPaperVersion() + "");
					if(null != apiResponse && null != apiResponse.getData()){
						vo.setObjectiveScore(apiResponse.getData().getKgScore());
						vo.setObjectiveScoreStr(getObjectiveScoreStr(apiResponse.getData().getObjectiveItemScoreList()));
                        vo.setObjectiveOmrStr(getObjectiveOmrStr(apiResponse.getData().getObjectiveItemScoreList()));
                    }
                }
                vos.add(vo);
            }
        }
        return vos;
    }
	
	
	@Override
	public Map<String, List<ObjectiveScoreVo>> getObjectiveScoreVoMaps(String projectId, List<SubjectVo> subjectVos) throws Exception {
		Map<String, List<ObjectiveScoreVo>> objectiveScoreVoMap= new HashMap<String, List<ObjectiveScoreVo>>();
		if(CollectionUtils.isEmpty(subjectVos)) {
			return objectiveScoreVoMap;
		}
		for (SubjectVo subjectVo : subjectVos) {
			//考生
			List<Student> students = studentMapper.selectByProjectAndSubjectId(projectId, subjectVo.getOrgSubjectId());
			if (CollectionUtils.isEmpty(students)) {
				continue;
			}
			
	        List<Paper> paperList = paperMapper.selectBySubject(projectId, subjectVo.getOrgSubjectId());
	        List<ObjectiveScoreVo> vos = new ArrayList<>(students.size());
	        //判断为试卷包
	        if(!CollectionUtils.isEmpty(paperList)) {
	        	Paper subjectPaper = paperList.get(0);
	            for (Student student : students) {
	            	ObjectiveScoreVo vo = new ObjectiveScoreVo();
                    vo.setAdmissionNum(student.getAdmissionNum());
                    vo.setStudentName(student.getStudentName());
					vo.setSubjectId(StringUtils.isNotEmpty(subjectVo.getOrgSubjectId()) ? subjectVo.getOrgSubjectId().substring(0, 6) : null);
					ApiResponse<ObjectiveScoreVO> apiResponse = kwClient.getKgScoreForPaper(student.getProjectId(), student.getAdmissionNum(), subjectPaper.getPaperId(), subjectPaper.getPaperVersion() + "");
					if(null != apiResponse && null != apiResponse.getData()){
                        vo.setObjectiveScore(apiResponse.getData().getKgScore());
						vo.setObjectiveScoreStr(getObjectiveScoreStr(apiResponse.getData().getObjectiveItemScoreList()));
                        vo.setObjectiveOmrStr(getObjectiveOmrStr(apiResponse.getData().getObjectiveItemScoreList()));
					}
					vo.setEncodeId(student.getAdmissionNum());
					vo.setSignUpId(student.getAdmissionNum());
                    vos.add(vo);
	            }
	        }
	        objectiveScoreVoMap.put(subjectVo.getOrgSubjectId(), vos);
        }
        return objectiveScoreVoMap;
    }
	
	
	


    private  String  getObjectiveScoreStr(List<ObjectiveItemScoreVO> objectiveItemScoreList){
 	        if(CollectionUtils.isEmpty(objectiveItemScoreList)){
				return null;
			}
			List<String> scoreList = new ArrayList<>(objectiveItemScoreList.size());
			for(ObjectiveItemScoreVO objectiveItemScore : objectiveItemScoreList){
				if( null == objectiveItemScore || null == objectiveItemScore.getScore()){
					scoreList.add("0");

				}else{
					scoreList.add(objectiveItemScore.getScore().stripTrailingZeros().toPlainString());
				}
			}
			if(!CollectionUtils.isEmpty(scoreList)){
				return String.join(",", scoreList);
			}

		return null;
	}

    private  String  getObjectiveOmrStr(List<ObjectiveItemScoreVO> objectiveItemScoreList){
        if(CollectionUtils.isEmpty(objectiveItemScoreList)){
            return null;
        }
        List<String> answerList = new ArrayList<>(objectiveItemScoreList.size());
        for(ObjectiveItemScoreVO objectiveItemScore : objectiveItemScoreList){
            if( null == objectiveItemScore || StringUtils.isEmpty(objectiveItemScore.getAnswer())){
                answerList.add("#");

            }else{
                answerList.add(objectiveItemScore.getAnswer());
            }
        }
        if(!CollectionUtils.isEmpty(answerList)){
            return String.join(",", answerList);
        }

        return null;
    }

	@Override
	public List<InterfaceDocVo> getInterfaceDocVos(String projectId, String officeId) throws Exception {
		List<InterfaceDocVo> result = new ArrayList<>();
		StopWatch stopWatch = StopWatch.create("生成考生图片统计：" + projectId);
		
		stopWatch.start("04-00-01 获取改考试下所有考生数据");
		log.info("# 04-00-01 接口文档数据：根据projectId：{}，获取改考试下所有考生数据", projectId);
		// 1.根据projectId获取改考试下所有考生数据
		List<Student> students = studentMapper.selectByProject(projectId);
		stopWatch.stop();

		stopWatch.start("04-00-02 获取组装所有考生的试卷信息及答案");
		log.info("# 04-00-02 接口文档数据：根据projectId：{}，获取组装所有考生的试卷信息及答案", projectId);
		// 2.获取组装所有考生的试卷信息及答案
		List<StudentAnswer> studentAnswers = studentAnswerMapper.selectByProject(projectId);
		Map<String, String> studentAnswerMap = new HashMap<String, String>();
		for (StudentAnswer answer : studentAnswers) {
			// 准考证+试题号检索答案
			studentAnswerMap.put(StringUtils.join(answer.getAdmissionNum(), answer.getItemId()), answer.getAnswer());
		}
		stopWatch.stop();
		
		stopWatch.start("04-00-03 遍历组装考生信息");
		List<StudentAnswerVo> studentAnswerVos = new ArrayList<>();
		// 考场号
		Integer roomIdStart = 0;
		for (int i = 0; i < students.size(); i++) {
			log.info("# 04-00-03 接口文档数据：根据projectId：{}，开始遍历组装第{}条考生信息，总共{}条数据", projectId, i, students.size());
			Student student = students.get(i);
			StudentAnswerVo studentAnswerVo = new StudentAnswerVo();
			// 可能是试卷也可能是试卷包
			List<Paper> papers = paperService.lambdaQuery()
					.eq(Paper::getProjectId, projectId)
					.eq(Paper::getId, student.getPaperId())
					.eq(Paper::getPaperVersion, student.getPaperVersion())
					.list();
			if (CollectionUtils.isEmpty(papers)) {
				papers = paperService.lambdaQuery()
						.eq(Paper::getPackageId, student.getPackageId())
						.eq(Paper::getPackageVersion, student.getPackageVersion())
						.eq(Paper::getProjectId, projectId)
						.list();
			}
			// 多试卷视为多科目情况, 目前是放在一个文件中
			studentAnswerVo.setAdmissionNum(student.getAdmissionNum());
			studentAnswerVo.setProjectId(student.getProjectId());
			
			// 已20个人为一个考场去评卷
			if (i % globalConfig.getRoomSpace() == 0) {
				roomIdStart += 1;
			}
			studentAnswerVo.setRoomId(roomIdStart);
			
			List<PaperItemVo> paperItemVoAllSubject = new ArrayList<>();
			for (Paper paper : papers) {
				// 获取到各个科目试卷所有客观题，和考生关联并给答案赋值
				List<PaperItemVo> paperItemVos = getSubjectivePaperItemTree(paper.getId());
				for (PaperItemVo paperItemVo : paperItemVos) {
					setAnswerData(paperItemVo, student, paper, studentAnswerMap);
				}
				// 多个科目合并
				paperItemVoAllSubject.addAll(paperItemVos);
			}
			// 调接口生成图片
			// 接口文档接口数据
			Callable<List<PaperItemVo>> interfaceDocVoCallable = new Callable<List<PaperItemVo>>() {
				@Override
				public List<PaperItemVo> call() throws Exception {
					int retryNum = 0;
					boolean hasError = Boolean.FALSE;
					List<PaperItemVo> paperItemVoAllSubjectTemp = null;
					DateTime startDateTime = DateUtil.date();
					log.info("# 04-01、开始生成{}考生{}答题图片", student.getStudentName(), student.getAdmissionNum());
					do {
						retryNum += 1;
						hasError = Boolean.FALSE;
						try {
							paperItemVoAllSubjectTemp = imageService.getImagesByPaper(paperItemVoAllSubject);
						} catch (Exception e) {
							hasError = Boolean.TRUE;
							if (retryNum > globalConfig.getRetryNum()) {
								log.error("# 04-01-02、异常生成{}考生{}答题图片，重试次数超过最大限制{}次", student.getStudentName(), student.getAdmissionNum(), globalConfig.getRetryNum(), e);
								throw e;
							}
							log.error("# 04-01-01、异常生成{}考生{}答题图片，进行重试第{}次", student.getStudentName(), student.getAdmissionNum(), retryNum, e);
						}
					} while (hasError);
					DateTime endDateTime = DateUtil.date();
					log.info("# 04-02、结束生成{}考生{}答题图片,消耗时间:{}", student.getStudentName(), student.getAdmissionNum(), DateUtil.formatBetween(startDateTime, endDateTime));
					return paperItemVoAllSubjectTemp;
				}
			};
			studentAnswerVo.setInterfaceDocVoCallable(interfaceDocVoCallable);
			studentAnswerVos.add(studentAnswerVo);
		}
		stopWatch.stop();

		stopWatch.start("04-03 生成考生答题图片");
		// 文件生成开始时间
		DateTime startImgTime = DateUtil.date();
		log.info("# 04-03、开始生成考生答题图片:{}", projectId);
		AtomicInteger studentImageFinishCount = new AtomicInteger(CommonConstant.ZERO);
		List<List<StudentAnswerVo>> interfaceDocVoCallablePartition = Lists.partition(studentAnswerVos, globalConfig.getBatchExamineSize());
		List<Future<List<StudentAnswerVo>>> futureList = interfaceDocVoCallablePartition.stream()
				.map(data -> {
					return ExecutorPoolUtil.getFileCreatePoolTaskExecutor().submit((Callable<List<StudentAnswerVo>>) () -> {
						for (StudentAnswerVo answerVo : data) {
							List<PaperItemVo> paperItemVos = answerVo.getInterfaceDocVoCallable().call();
							answerVo.setPaperItemVos(paperItemVos);
							log.info("# 04-03-01、第{}号考生答题图片生成完成，总共{}号考生:{}", studentImageFinishCount.incrementAndGet(), studentAnswerVos.size(), projectId);
						}
						return data;
					});
				}).collect(Collectors.toList());
		for (Future<List<StudentAnswerVo>> listFuture : futureList) {
			listFuture.get();
		}
		// 文件结束时间
		DateTime endImgTime = DateUtil.date();
		log.info("# 04-04、结束生成考生答题图片：{}，每段{}条数据，数据分{}段，总耗时：{}", projectId, globalConfig.getBatchExamineSize(), interfaceDocVoCallablePartition.size(), DateUtil.formatBetween(startImgTime, endImgTime));
		stopWatch.stop();
		
		stopWatch.start("04-04 生成最后的接口文件实体");
		// 3.生成最终的接口文件
		for (StudentAnswerVo studentAnswerVo : studentAnswerVos) {
			List<PaperItemVo> paperItemVos = studentAnswerVo.getPaperItemVos();
			studentAnswerVo.setPaperItemVos(paperItemVos);
			int i = 0;
			for (PaperItemVo paperItemVo : paperItemVos) {
				InterfaceDocVo interfaceDocVo = new InterfaceDocVo();
				interfaceDocVo.setAdmissionNum(studentAnswerVo.getAdmissionNum());
				interfaceDocVo.setSubjectId(paperItemVo.getSubjectId());
				interfaceDocVo.setRoomId(studentAnswerVo.getRoomId());
				interfaceDocVo.setPieceItemId(paperItemVo.getOrderNum());
				interfaceDocVo.setAbsoluteImage(paperItemVo.getAnswerImgFile());
				interfaceDocVo.setImageNumber(++i);
				result.add(interfaceDocVo);
			}
		}
		stopWatch.stop();
		logger.info("# 04-05、{}操作完整时间：{}{}", projectId, FileUtil.getLineSeparator(), stopWatch.prettyPrint(TimeUnit.SECONDS));

		return result;
	}
	
	
	@Override
	public Map<String, List<InterfaceDocVo>> getInterfaceDocVoMaps(String projectId, List<SubjectVo> subjectVos, Map<String, List<StudentVo>> studenMap) throws Exception {
		Map<String, List<InterfaceDocVo>> interfaceDocVoMaps = new HashMap<String, List<InterfaceDocVo>>();
		if(CollectionUtils.isEmpty(subjectVos)) {
			return interfaceDocVoMaps;
		}
		for (SubjectVo subjectVo : subjectVos) {
			List<InterfaceDocVo> result = new ArrayList<>();
			// 1.根据projectId获取改考试下所有考生数据
			List<StudentVo> students =  studenMap.get(subjectVo.getOrgSubjectId());
			
			// 2.获取组装所有考生的试卷信息及答案
			List<StudentAnswer> studentAnswers = studentAnswerMapper.selectByProjectAndSubjectId(projectId, subjectVo.getOrgSubjectId());
			
			Map<String, String> studentAnswerMap = new HashMap<String, String>();
			for (StudentAnswer answer : studentAnswers) {
				// 准考证+试题号检索答案
				studentAnswerMap.put(StringUtils.join(answer.getAdmissionNum(), answer.getItemId()), answer.getAnswer());
			}
			List<StudentAnswerVo> studentAnswerVos = new ArrayList<>();
			for (int i = 0; i < students.size(); i++) {
				StudentVo student = students.get(i);
				StudentAnswerVo studentAnswerVo = new StudentAnswerVo();
				List<Paper> papers = paperService.lambdaQuery()
						.eq(Paper::getProjectId, projectId)
						.eq(Paper::getSubjectId, subjectVo.getOrgSubjectId())
						.eq(Paper::getId, student.getPaperId())
						.eq(Paper::getPaperVersion, student.getPaperVersion())
						.list();
				if (CollectionUtils.isEmpty(papers)) {
					 papers = paperMapper.selectBySubject(projectId, subjectVo.getOrgSubjectId());
				}
				// 多试卷视为多科目情况, 目前是放在一个文件中
				studentAnswerVo.setAdmissionNum(student.getAdmissionNum());
				studentAnswerVo.setProjectId(projectId);
				studentAnswerVo.setRoomId(Integer.valueOf(student.getRoomId()));
				List<PaperItemVo> paperItemVoAllSubject = new ArrayList<>();
				for (Paper paper : papers) {
					// 获取到各个科目试卷所有客观题，和考生关联并给答案赋值
					List<PaperItemVo> paperItemVos = getSubjectivePaperItemTree(paper.getId());
					for (PaperItemVo paperItemVo : paperItemVos) {
						setAnswerData(paperItemVo, student, paper, studentAnswerMap);
					}
					// 多个科目合并
					paperItemVoAllSubject.addAll(paperItemVos);
				}
				// 调接口生成图片
				// 接口文档接口数据
				Callable<List<PaperItemVo>> interfaceDocVoCallable = new Callable<List<PaperItemVo>>() {
					@Override
					public List<PaperItemVo> call() throws Exception {
						int retryNum = 0;
						boolean hasError = Boolean.FALSE;
						List<PaperItemVo> paperItemVoAllSubjectTemp = null;
						DateTime startDateTime = DateUtil.date();
						log.info("# 04-01、开始生成{}考生{}答题图片", student.getStudentName(), student.getAdmissionNum());
						do {
							retryNum += 1;
							hasError = Boolean.FALSE;
							try {
								paperItemVoAllSubjectTemp = imageService.getImagesByPaper(paperItemVoAllSubject);
							} catch (Exception e) {
								hasError = Boolean.TRUE;
								if (retryNum > globalConfig.getRetryNum()) {
									log.error("# 04-01-02、异常生成{}考生{}答题图片，重试次数超过最大限制{}次", student.getStudentName(), student.getAdmissionNum(), globalConfig.getRetryNum(), e);
									throw e;
								}
								log.error("# 04-01-01、异常生成{}考生{}答题图片，进行重试第{}次", student.getStudentName(), student.getAdmissionNum(), retryNum, e);
							}
						} while (hasError);
						DateTime endDateTime = DateUtil.date();
						log.info("# 04-02、结束生成{}考生{}答题图片,消耗时间:{}", student.getStudentName(), student.getAdmissionNum(), DateUtil.formatBetween(startDateTime, endDateTime));
						return paperItemVoAllSubjectTemp;
					}
				};
				studentAnswerVo.setInterfaceDocVoCallable(interfaceDocVoCallable);
				studentAnswerVos.add(studentAnswerVo);
			}
			// 文件生成开始时间
			DateTime startImgTime = DateUtil.date();
			log.info("# 04-03、开始生成考生答题图片:{}", projectId);
			List<List<StudentAnswerVo>> interfaceDocVoCallablePartition = Lists.partition(studentAnswerVos, globalConfig.getBatchExamineSize());
			List<Future<List<StudentAnswerVo>>> futureList = interfaceDocVoCallablePartition.stream()
					.map(data -> {
						return ExecutorPoolUtil.getFileCreatePoolTaskExecutor().submit((Callable<List<StudentAnswerVo>>) () -> {
							for (StudentAnswerVo answerVo : data) {
								List<PaperItemVo> paperItemVos = answerVo.getInterfaceDocVoCallable().call();
								answerVo.setPaperItemVos(paperItemVos);
							}
							return data;
						});
					}).collect(Collectors.toList());
			for (Future<List<StudentAnswerVo>> listFuture : futureList) {
				listFuture.get();
			}
			// 文件结束时间
			DateTime endImgTime = DateUtil.date();
			log.info("# 04-04、结束生成考生答题图片：{}，每段{}条数据，数据分{}段，总耗时：{}", projectId, globalConfig.getBatchExamineSize(), interfaceDocVoCallablePartition.size(), DateUtil.formatBetween(startImgTime, endImgTime));

			// 3.生成最终的接口文件
			for (StudentAnswerVo studentAnswerVo : studentAnswerVos) {
				List<PaperItemVo> paperItemVos = studentAnswerVo.getPaperItemVos();
				studentAnswerVo.setPaperItemVos(paperItemVos);
				int i = 0;
				for (PaperItemVo paperItemVo : paperItemVos) {
					InterfaceDocVo interfaceDocVo = new InterfaceDocVo();
					interfaceDocVo.setAdmissionNum(studentAnswerVo.getAdmissionNum());
					interfaceDocVo.setSubjectId(subjectVo.getOrgSubjectId().substring(0, 6));
					interfaceDocVo.setRoomId(studentAnswerVo.getRoomId());
					interfaceDocVo.setPieceItemId(paperItemVo.getOrderNum());
					interfaceDocVo.setAbsoluteImage(paperItemVo.getAnswerImgFile());
					interfaceDocVo.setImageNumber(++i);
					result.add(interfaceDocVo);
				}
			}
			interfaceDocVoMaps.put( subjectVo.getOrgSubjectId(), result);
		}
		return interfaceDocVoMaps;
	}
	
	
	 

	/**
	 * 递归填充答案等信息
	 *
	 * @param paperItemVo
	 * @param student
	 * @param studentAnswerMap
	 */
	private void setAnswerData(PaperItemVo paperItemVo, Student student, Paper paper, Map<String, String> studentAnswerMap){
		paperItemVo.setPaperId(paper.getPaperId());
		paperItemVo.setProjectId(paper.getProjectId());
		paperItemVo.setSubjectId(StringUtils.isEmpty(paper.getPaperId()) ? null : paper.getPaperId().substring(0, 6));
		paperItemVo.setAdmissionNum(student.getAdmissionNum());
		if(CollectionUtils.isEmpty(paperItemVo.getChildren())){
			// 查询答案
			String itemId = paperItemVo.getTopicId();
			String answerContent = studentAnswerMap.get(StringUtils.join(student.getAdmissionNum(), itemId));
			paperItemVo.setStudentAnswer(answerContent);
		} else {
			// 否则复合体
			List<PaperItemVo> compoundItem = paperItemVo.getChildren();
			for (PaperItemVo itemVo : compoundItem) {
				setAnswerData(itemVo, student, paper, studentAnswerMap);
			}
		}
	}
	
	
	/**
	 * 递归填充答案等信息
	 *
	 * @param paperItemVo
	 * @param student
	 * @param studentAnswerMap
	 */
	private void setAnswerData(PaperItemVo paperItemVo, StudentVo student, Paper paper, Map<String, String> studentAnswerMap){
		paperItemVo.setPaperId(paper.getPaperId());
		paperItemVo.setProjectId(paper.getProjectId());
		paperItemVo.setSubjectId(StringUtils.isEmpty(paper.getPaperId()) ? null : paper.getPaperId().substring(0, 6));
		paperItemVo.setAdmissionNum(student.getAdmissionNum());
		if(CollectionUtils.isEmpty(paperItemVo.getChildren())){
			// 查询答案
			String itemId = paperItemVo.getTopicId();
			String answerContent = studentAnswerMap.get(StringUtils.join(student.getAdmissionNum(), itemId));
			paperItemVo.setStudentAnswer(answerContent);
		} else {
			// 否则复合体
			List<PaperItemVo> compoundItem = paperItemVo.getChildren();
			for (PaperItemVo itemVo : compoundItem) {
				setAnswerData(itemVo, student, paper, studentAnswerMap);
			}
		}
	}


	@Override
	public List<StudentVo> getStudentVos(String projectId) throws Exception {
		//单场考试
		LambdaQueryWrapper<Student> studentQueryWrapper = new LambdaQueryWrapper<>();
		studentQueryWrapper.eq(Student::getProjectId, projectId);
		List<Student> students = studentMapper.selectList(studentQueryWrapper);
		if (CollectionUtils.isEmpty(students)) {
			return Collections.emptyList();
		}
		//试卷包情况
		List<Paper> packageList = new ArrayList<Paper>();
		Set<String> packageIds  = students.stream().filter(s -> StringUtils.isNotEmpty(s.getPackageId())).map(s -> s.getPackageId()).collect(Collectors.toSet());
		if(!CollectionUtils.isEmpty(packageIds)) {
			LambdaQueryWrapper<Paper> paperQueryWrapper = new LambdaQueryWrapper<>();
			paperQueryWrapper.eq(Paper::getProjectId,projectId).in(Paper::getPackageId, packageIds);
			packageList = paperMapper.selectList(paperQueryWrapper);
		}
		List<StudentVo> vos = new ArrayList<>(students.size());
		//处理试卷包：考生都需要考
		if(!CollectionUtils.isEmpty(packageList)){
			Map<String, List<Paper>> paperMap = packageList.stream().collect(Collectors.groupingBy(p -> p.getProjectId() + p.getSubjectId() + p.getPackageId()));
			for (Student student : students) {
				if(CollectionUtils.isEmpty(paperMap) ){
					continue;
				}
				List<Paper> papers = paperMap.get(student.getProjectId() + student.getSubjectId() + student.getPackageId());
				if(CollectionUtils.isEmpty(papers) ){
					continue;
				}
				for (Paper paper :papers) {
						StudentVo vo = new StudentVo();
						vo.setAdmissionNum(student.getAdmissionNum());
						vo.setStudentName(student.getStudentName());
						vo.setIdCard(student.getIdNo());
						vo.setSubjectId(StringUtils.isNotEmpty(paper.getPaperId()) ? paper.getPaperId().substring(0, 6) : null);
						vos.add(vo);
				}
			}

		}else{
			//非试卷包
			LambdaQueryWrapper<Paper> paperWrapper = new LambdaQueryWrapper<>();
			paperWrapper.eq(Paper::getProjectId, projectId);
			List<Paper> papers = paperMapper.selectList(paperWrapper);
			Map<String, String> paperMap = papers.stream().collect(Collectors.toMap(p -> (p.getProjectId() + p.getSubjectId() + p.getId()), p -> p.getPaperId(), (key1, key2) -> key2));
			for (Student student : students) {
				StudentVo vo = new StudentVo();
				vo.setAdmissionNum(student.getAdmissionNum());
				vo.setStudentName(student.getStudentName());
				vo.setIdCard(student.getIdNo());
				if (!CollectionUtils.isEmpty(paperMap) && null != paperMap.get(student.getProjectId() + student.getSubjectId()+student.getPaperId())) {
					vo.setSubjectId(paperMap.get(student.getProjectId() + student.getSubjectId()+student.getPaperId()).substring(0, 6));
				}

				vos.add(vo);
			}
		}
		return vos;
	}
	
	
	@Override
	public Map<String, List<StudentVo>> getStudentVosMap(String projectId, List<SubjectVo> subjectVos) throws Exception {
		Map<String, List<StudentVo>> studentVosMap= new HashMap<String, List<StudentVo>>();
		if(CollectionUtils.isEmpty(subjectVos)) {
			return studentVosMap;
		}
		for (SubjectVo subjectVo : subjectVos) {
			//单场考试
			List<Student> students = studentMapper.selectByProjectAndSubjectId(projectId, subjectVo.getSubjectId());
			if (CollectionUtils.isEmpty(students)) {
				continue;
			}
			List<StudentVo> vos = new ArrayList<>(students.size());
			// 考场号
			Integer roomIdStart = 0;
			for (int i = 0; i < students.size(); i++) {
				StudentVo vo = new StudentVo();
				vo.setAdmissionNum(students.get(i).getAdmissionNum());
				vo.setStudentName(students.get(i).getStudentName());
				vo.setIdCard(students.get(i).getIdNo());
				vo.setSubjectId(StringUtils.isNotEmpty(subjectVo.getSubjectId()) ? subjectVo.getSubjectId().substring(0, 6) : null);
				// 已20个人为一个考场去评卷
				if (i % globalConfig.getRoomSpace() == 0) {
					roomIdStart += 1;
				}
				vo.setRoomId(String.valueOf(roomIdStart));
				// 区域设置深证市,目前写死
				vo.setArea(globalConfig.getRoomArea());
				vo.setPaperId(students.get(i).getPaperId());
				vo.setPaperVersion(students.get(i).getPaperVersion());
				vos.add(vo);
			}
			studentVosMap.put(subjectVo.getSubjectId(), vos);
		}
		return studentVosMap;
	}
	

	/****
	 * 获取试卷的主观题树
	 * 
	 * @param paperId
	 * @return
	 * @throws Exception
	 */
	@Override
	public List<PaperItemVo> getSubjectivePaperItemTree(String paperId) throws Exception {
		// 获取试卷下面所有的题目
		// 大题下面所有的小题
		List<PaperItemVo> paperItemsTopicChilds = new ArrayList<PaperItemVo>();
		List<PaperItem> paperItems = paperItemMapper.selectByPaperId(paperId);
		List<PaperItemVo> paperItemsVosTemp = new ArrayList<PaperItemVo>();
		List<PaperItemVo> paperItemsVos = PjBeanUtils.convertList(PaperItemVo.class, paperItems);
		paperItemsVosTemp.addAll(paperItemsVos);
		
		List<PaperItemVo> topicPaperItems = new ArrayList<PaperItemVo>();
		int topicIndex = 0;
		for (Iterator<PaperItemVo> iterator = paperItemsVosTemp.iterator(); iterator.hasNext();) {
			PaperItemVo paperItem =  iterator.next(); 
			if("topic".equals(paperItem.getItemType())) {
				topicPaperItems.add(topicIndex, paperItem);
				topicIndex ++ ;
			}
		}
		
		// 剔除掉所有的非主观题
		if (!CollectionUtils.isEmpty(paperItemsVos)) {
			for (Iterator<PaperItemVo> iterator = paperItemsVos.iterator(); iterator.hasNext();) {
				PaperItemVo paperItem = iterator.next();
				List<String> subItemTypes = getSubItemType();
				if (!subItemTypes.contains(paperItem.getItemType())) {
					iterator.remove();
				}
			}
		}
				
		if (CollectionUtils.isEmpty(paperItemsVosTemp)) {
			return paperItemsVos;
		}
		// 迭代每一个大题
		for (Iterator<PaperItemVo> topiciterator = topicPaperItems.iterator(); topiciterator.hasNext();) {
			PaperItemVo paperItem =  topiciterator.next();
			List<PaperItem> topChileItems = paperItemMapper.selectByPaperIdByParentId(paperId, paperItem.getId());
			List<PaperItemVo> topChileItemsTemp = PjBeanUtils.convertList(PaperItemVo.class, topChileItems);
			if(!CollectionUtils.isEmpty(topChileItemsTemp)) {
				for (PaperItemVo topChileItemTemp : topChileItemsTemp) {
					// 如果是简答题
					if (SubjectiveItemTypeEnum.short_answer.getCode().equals(topChileItemTemp.getItemType())) {
						paperItemsTopicChilds.add(topChileItemTemp);
					}
					// 如果是图片做答题
					if (SubjectiveItemTypeEnum.picture_answer.getCode().equals(topChileItemTemp.getItemType())) {
						paperItemsTopicChilds.add(topChileItemTemp);
					}
					// 如果是填空题
					if (SubjectiveItemTypeEnum.fill_in_blanks.getCode().equals(topChileItemTemp.getItemType())) {
						List<PaperItemVo> children = new ArrayList<>();
						int subIndex = 1;
						for (PaperItemVo paperItemVo : paperItemsVosTemp) {
							if (paperItemVo.getParentId().equals(topChileItemTemp.getId())) {
								paperItemVo.setOrderNum(subIndex++);
								children.add(paperItemVo);
							}
						}
						topChileItemTemp.setChildren(children);
						paperItemsTopicChilds.add(topChileItemTemp);
					}
					// 如果是复合题
					if (SubjectiveItemTypeEnum.compound.getCode().equals(topChileItemTemp.getItemType())) {
						// 首先看复合题下面有没有主观题
						int subjectSize = 0;
						for (PaperItemVo paperItemVo : paperItemsVosTemp) {
							if (paperItemVo.getParentId().equals(topChileItemTemp.getId()) && SubjectiveItemTypeEnum.short_answer.getCode().equals(paperItemVo.getItemType())) {
								subjectSize ++;
							}
						}
						// 如果复合题下面没有主观简答题则抛弃该复合题
						if(subjectSize == 0) {
							continue;
						}
						// 处理复合题下面的主观题
						List<PaperItemVo> children = new ArrayList<>();
						int subIndex = 1;
						for (PaperItemVo paperItemVo : paperItemsVosTemp) {
							if (paperItemVo.getParentId().equals(topChileItemTemp.getId()) && SubjectiveItemTypeEnum.short_answer.getCode().equals(paperItemVo.getItemType())) {
								paperItemVo.setOrderNum(subIndex++);
								children.add(paperItemVo);
							}
						}
						// 如果有简答题则保留该复合题
						if (!CollectionUtils.isEmpty(children)) {
							topChileItemTemp.setChildren(children);
							BigDecimal compoundSubjectiveAllscore = new BigDecimal("0");
							for (PaperItemVo child : children) {
								// 获取每一道小题的总分数
								compoundSubjectiveAllscore = compoundSubjectiveAllscore.add(child.getScore());
							}
							topChileItemTemp.setScore(compoundSubjectiveAllscore);
							paperItemsTopicChilds.add(topChileItemTemp);
						}
					}
				}
			}
		}
		int index = 1;
		if (!CollectionUtils.isEmpty(paperItemsTopicChilds)) {
			for (Iterator<PaperItemVo> iterator = paperItemsTopicChilds.iterator(); iterator.hasNext();) {
				PaperItemVo paperItemVo = iterator.next();
				paperItemVo.setOrderNum(index++);
			}
		}
		return paperItemsTopicChilds;
	}

	private List<String> getSubItemType() {
		List<String> subItemType = new ArrayList<String>();
		for (SubjectiveItemTypeEnum subjectiveItemTypeEnum : SubjectiveItemTypeEnum.values()) {
			subItemType.add(subjectiveItemTypeEnum.getCode());
		}
		subItemType.add("option");
		return subItemType;
	}


	@Override
	public List<TopicVO> getTopicList(String paperId) throws Exception {
		List<PaperItemVo> paperItemVoList = getSubjectivePaperItemTree(paperId);
		List<TopicVO> voList = new ArrayList<>();
		Integer topicCode = 1;
		for (PaperItemVo paperItemVo : paperItemVoList) {
			TopicVO topicVO = new TopicVO();
			topicVO.setTopicCode(topicCode);
			topicVO.setScore(paperItemVo.getScore().stripTrailingZeros().toPlainString());
			topicVO.setSubjectId(paperItemVo.getPaperId());
			topicVO.setScoreStep(BigDecimal.ZERO);
			topicVO.setTopicName(topicCode + "、" + getTopicName(paperItemVo.getItemType()));
			// 默认单评
			topicVO.setScoreType(1);
			// 如果是复合题，判断是否有多个主观题，如果是，则多评
			if (SubjectiveItemTypeEnum.compound.getCode().equals(paperItemVo.getItemType())) {
				// 复合题的子小题
				if (null != paperItemVo.getChildren()) {
					List<TopicItemVO> itemVOList = new ArrayList<>();
					Integer itemId = 1;
					for (PaperItemVo itemVo : paperItemVo.getChildren()) {
						TopicItemVO vo = new TopicItemVO();
						vo.setTopicId(topicCode);
						vo.setScore(itemVo.getScore().toPlainString());
						vo.setScoreStep(this.scoreStep(itemVo.getScore()));
						vo.setEnumType("A");
						vo.setScoreMin(BigDecimal.valueOf(0.5));
						vo.setStartScore(new BigDecimal("0.0"));
						vo.setSubjectId(paperItemVo.getPaperId());
						double id = BigDecimal.valueOf(itemId.doubleValue()).divide(new BigDecimal(100)).doubleValue();
						vo.setItemId(topicCode.doubleValue() + id);
						String itemName = SubjectiveItemTypeEnum.getNameByCode(itemVo.getItemType());
						vo.setItemName(vo.getItemId() + (StringUtils.isEmpty(itemName) ? getTopicName(paperItemVo.getItemType()) : itemName));
						itemVOList.add(vo);
						itemId++;
					}
					topicVO.setItemVOList(itemVOList);
				}
			} else {
				// 如果是非复合题，大题也是小题
				List<TopicItemVO> itemVOList = new ArrayList<>();
				if (null != paperItemVo.getChildren() && paperItemVo.getChildren().size() > 0) {
					Integer itemId = 1;
					for (PaperItemVo itemVo : paperItemVo.getChildren()) {
						TopicItemVO vo = new TopicItemVO();
						vo.setTopicId(topicCode);
						vo.setScore(itemVo.getScore().toPlainString());
						vo.setScoreStep(this.scoreStep(itemVo.getScore()));
						vo.setEnumType("A");
						vo.setScoreMin(BigDecimal.valueOf(0.5));
						vo.setStartScore(new BigDecimal("0.0"));
						vo.setSubjectId(paperItemVo.getPaperId());
						double id = BigDecimal.valueOf(itemId.doubleValue()).divide(new BigDecimal(100)).doubleValue();
						vo.setItemId(topicCode.doubleValue() + id);
						String itemName = SubjectiveItemTypeEnum.getNameByCode(itemVo.getItemType());
						vo.setItemName(vo.getItemId() + (StringUtils.isEmpty(itemName) ? getTopicName(paperItemVo.getItemType()) : itemName));
						itemVOList.add(vo);
						itemId++;
					}
				} else {
					TopicItemVO vo = new TopicItemVO();
					vo.setTopicId(topicCode);
					vo.setScore(topicVO.getScore());
					vo.setScoreStep(this.scoreStep(new BigDecimal(topicVO.getScore())));
					vo.setEnumType("A");
					vo.setScoreMin(BigDecimal.valueOf(0.5));
					vo.setStartScore(new BigDecimal("0.0"));
					vo.setSubjectId(paperItemVo.getPaperId());
					double id = BigDecimal.valueOf(1).divide(new BigDecimal(100)).doubleValue();
					vo.setItemId(topicCode.doubleValue() + id);
					String itemName = SubjectiveItemTypeEnum.getNameByCode(paperItemVo.getItemType());
					vo.setItemName(vo.getItemId() + (StringUtils.isEmpty(itemName) ? getTopicName(paperItemVo.getItemType()) : itemName));
					itemVOList.add(vo);
				}
				topicVO.setItemVOList(itemVOList);
			}

			voList.add(topicVO);
			topicCode++;
		}
		return voList;
	}

	private String getTopicName(String itemType) {
		String defaultStr = "复合题";
		if (StringUtils.isEmpty(itemType)) {
			return defaultStr;
		}
		String desc = SubjectiveItemTypeEnum.getNameByCode(itemType);
		if (StringUtils.isNotEmpty(desc)) {
			return desc;
		}
		return defaultStr;

	}

	/**
	 * @Params:
	 * @Description: 获取间隔分数
	 * @Author: STARF
	 * @CreateDate: 2022/4/6 9:35
	 **/
	public String scoreStep(BigDecimal score) {
		StringBuilder step = new StringBuilder();
		step.append("0,");
		BigDecimal st = BigDecimal.ZERO;
		while (st.compareTo(score) < 0) {
			st = st.add(BigDecimal.valueOf(0.5));
			step.append(st.stripTrailingZeros().toPlainString()).append(",");
		}

		return step.toString();
	}
	
	
	/**
     * 使用永久凭证，单例模式
     *
     * @return
     */
    public synchronized ObsClient getObsClient() {
        if (obsClient == null) {
            Bucket bucket = getBucketConfig();
            ObsConfiguration config = new ObsConfiguration();
            config.setSocketTimeout(30000);
            config.setConnectionTimeout(10000);
            config.setEndPoint(bucket.getEndPoint());
            // 查找默认配置
            synchronized (this) {
                if (obsClient == null) {
                    obsClient = new ObsClient(bucket.getAccessKey(), bucket.getSecretKey(), config);
                }
            }
        }
        return obsClient;
    }

    @Override
	public Bucket getBucketConfig() {
		Bucket bucket = null;
		try {
			String json = tpsClient.getBucketTempCredential();
			if(StringUtils.isNotEmpty(json)) {
				bucket = JSON.parseObject(json, Bucket.class);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bucket;
	}
}
